package tman.simulator.core;

import Monitor.TManMonitorServer;
import Monitor.TManMonitorServerInit;
import common.configuration.AggregationConfiguration;
import common.configuration.CyclonConfiguration;
import common.configuration.Global;
import common.configuration.MonitorConfiguration;
import common.configuration.TManConfiguration;
import common.simulation.SimulatorInit;
import common.simulation.SimulatorPort;
import common.simulation.scenarios.Scenario3;

import java.io.IOException;
import java.util.HashMap;

import se.sics.kompics.ChannelFilter;
import se.sics.kompics.Component;
import se.sics.kompics.ComponentDefinition;
import se.sics.kompics.Kompics;
import se.sics.kompics.address.Address;
import se.sics.kompics.network.Message;
import se.sics.kompics.network.Network;
import se.sics.kompics.network.NetworkConfiguration;
import se.sics.kompics.network.model.king.KingLatencyMap;
import se.sics.kompics.p2p.bootstrap.BootstrapConfiguration;
import se.sics.kompics.p2p.bootstrap.server.BootstrapServer;
import se.sics.kompics.p2p.bootstrap.server.BootstrapServerInit;
import se.sics.kompics.p2p.experiment.dsl.SimulationScenario;
import se.sics.kompics.p2p.simulator.P2pSimulator;
import se.sics.kompics.p2p.simulator.P2pSimulatorInit;
import se.sics.kompics.simulation.SimulatorScheduler;
import se.sics.kompics.timer.Timer;
import org.apache.log4j.PropertyConfigurator;


public final class TManSimulationMain extends ComponentDefinition {

	static {
			 PropertyConfigurator.configureAndWatch("log4j.properties");
		}
	private static SimulatorScheduler simulatorScheduler = new SimulatorScheduler();
	private static SimulationScenario scenario = SimulationScenario.load(System.getProperty("scenario"));

//-------------------------------------------------------------------	
	public static void main(String[] args) {
//		for(int i=0; i<args.length; i++)
//		{
//			System.out.println("args[" + i +"] = " + args[i]);
//		}
		
		if(args != null)
		{
			setGlobals(args, 4);
		}
		else
		{
			setGlobals(parseProperties(), 4);
		}

		Kompics.setScheduler(simulatorScheduler);
		Kompics.createAndStart(TManSimulationMain.class, 1);
	}

//-------------------------------------------------------------------	
	public TManSimulationMain() throws IOException {
		P2pSimulator.setSimulationPortType(SimulatorPort.class);

		// create
		Component bootstrapServer = create(BootstrapServer.class);
		Component p2pSimulator = create(P2pSimulator.class);
		Component simulator = create(TManSimulator.class);
		Component monitorServer = create(TManMonitorServer.class);

		// loading component configurations
		final BootstrapConfiguration bootConfiguration = BootstrapConfiguration.load(System.getProperty("bootstrap.configuration"));
		final CyclonConfiguration cyclonConfiguration = CyclonConfiguration.load(System.getProperty("cyclon.configuration"));
		final TManConfiguration applicationConfiguration = TManConfiguration.load(System.getProperty("tman.configuration"));
		final AggregationConfiguration aggregationConfiguration = 
                        AggregationConfiguration.load(System.getProperty("aggregation.configuration"));
		final NetworkConfiguration networkConfiguration = NetworkConfiguration.load(System.getProperty("network.configuration"));
		final MonitorConfiguration monitorConfiguration = MonitorConfiguration.load(System.getProperty("cyclon.monitor.configuration"));

		trigger(new TManMonitorServerInit(monitorConfiguration), monitorServer.getControl());
		trigger(new BootstrapServerInit(bootConfiguration), bootstrapServer.getControl());
		trigger(new P2pSimulatorInit(simulatorScheduler, scenario, new KingLatencyMap()), p2pSimulator.getControl());
		trigger(new SimulatorInit(bootConfiguration, 
                        cyclonConfiguration, applicationConfiguration,
                        aggregationConfiguration, monitorConfiguration, networkConfiguration.getAddress()), simulator.getControl());

		// connect
		connect(bootstrapServer.getNegative(Network.class), p2pSimulator.getPositive(Network.class), new MessageDestinationFilter(bootConfiguration.getBootstrapServerAddress()));
		connect(bootstrapServer.getNegative(Timer.class), p2pSimulator.getPositive(Timer.class));
		connect(simulator.getNegative(Network.class), p2pSimulator.getPositive(Network.class));
		connect(simulator.getNegative(Timer.class), p2pSimulator.getPositive(Timer.class));
		connect(simulator.getNegative(SimulatorPort.class), p2pSimulator.getPositive(SimulatorPort.class));
        connect(monitorServer.getNegative(Network.class), p2pSimulator
                .getPositive(Network.class), new MessageDestinationFilter(
                monitorConfiguration.getMonitorServerAddress()));
connect(monitorServer.getNegative(Timer.class), p2pSimulator
                .getPositive(Timer.class));
        }
	
//-------------------------------------------------------------------	
	private final static class MessageDestinationFilter extends ChannelFilter<Message, Address> {
		public MessageDestinationFilter(Address address) {
			super(Message.class, address, true);
		}

		public Address getValue(Message event) {
			return event.getDestination();
		}
	}
	
	private static void setGlobals(String[] args, int seed)
	{
		Global.odDistr = new HashMap<Integer, Integer>();
  	    Global.seed = seed; //Set seed for all RNGs used throughout the project
  	    
  	    Global.id = Integer.parseInt(args[0]);
  	    Global.sl = Integer.parseInt(args[1]);
  	    Global.vs = Integer.parseInt(args[2]);
  	    Global.tvs = Integer.parseInt(args[3]);
  	    Global.r = Integer.parseInt(args[args.length-1]);
  	    
  	    for(int i=4; i<args.length-1; i++)
  	    {
  	    	String[] temp = args[i].split(":");
  	    	Global.odDistr.put(Integer.parseInt(temp[0]), Integer.parseInt(temp[1]));
  	    }
	}
	
	private static String[] parseProperties()
	{
		String[] splits = System.getProperty("od").split(";");
		String[] properties = new String[splits.length + 5];
		properties[0] = System.getProperty("id");
		properties[1] = System.getProperty("sl");
		properties[2] = System.getProperty("vs");
		properties[3] = System.getProperty("tvs");
		properties[properties.length-1]= System.getProperty("r");
		for(int i=0; i<splits.length; i++)
		{
			properties[i+4] = splits[i];
		}
		
		return properties;
	}
}
